#include <ftp_session.h>

#include <ftp_util.h>
#include <reply.h>
#include <fmt/format.h>
#include <thread>
#include <sstream>
#include <event2/bufferevent_ssl.h>

void FTPSession::sendFtpMsg(int code, const std::string& msg)
{
	std::string reply = fmt::format("{} {}\r\n", code, msg);
	cmdConn_->send(reply.c_str(), reply.size());
	std::string log = fmt::format("send: {} {} -> {}", code, msg, cmdConn_->name());
	LOG_DEBUG << log;
}

bool FTPSession::checkLoginState() const
{
	if (loginState_ != State::LOGINED) {
		return false;
	}
	return true;
}

bool FTPSession::checkFilePermission(const std::string& filename)
{
	return user_ && user_->checkInRootDir(filename);
}

bool FTPSession::checkCmdPermission(const std::string& cmd)
{
	// All权限的指令，只要用户权限不是None都可以响应
	const static std::unordered_map<std::string, UserPermission> permissionMap =
	{
		{"AUTH",UserPermission::All},
		{"USER",UserPermission::All},
		{"PASS",UserPermission::All},
		{"QUIT",UserPermission::All},
		// TRANSFER PARAMETER COMMANDS
		{"PBSZ",UserPermission::All},
		{"PROT",UserPermission::All},
		{"PASV",UserPermission::All},
		{"TYPE",UserPermission::All},
		{"STRU",UserPermission::All},
		{"MODE",UserPermission::All},
		{"OPTS",UserPermission::All},
		{"SYST",UserPermission::All},
		{"FEAT",UserPermission::All},
		// FTP SERVICE COMMANDS
		{"RETR",UserPermission::FileRead},
		{"STOR",UserPermission::FileWrite},
		{"DELE",UserPermission::FileDelete},
		{"RNFR",UserPermission::FileRename},
		{"RNTO",UserPermission::FileRename},
		{"REST",UserPermission::FileRead|UserPermission::FileWrite},
		{"PWD", UserPermission::DirList},
		{"CWD", UserPermission::DirList},
		{"MKD", UserPermission::DirCreate},
		{"RMD", UserPermission::DirDelete},
		{"CDUP",UserPermission::DirList},
		{"LIST",UserPermission::DirList},
		{"SIZE",UserPermission::FileRead},
		{"NOOP",UserPermission::All},
		{"HELP",UserPermission::All}
	};
	auto permissionIt = permissionMap.find(cmd);
	if (permissionIt == permissionMap.end()) {
		LOG_DEBUG << "Unknown command: " << cmd;
		return false;
	}
	if (permissionIt->second == UserPermission::All) {
		return true;
	}
	return user_ && static_cast<int>(user_->getPermission() & permissionIt->second);
}

void FTPSession::handleDataConnTask(const std::function<void()>& task)
{
	if (dataConn_) {
		// 如果连接已经建立且为ssl，但是未完成ssl握手，需要向任务队列中添加任务，ssl握手完成后调用该回调
		if (dataConn_->isSSLConnection() && !dataConn_->isSSLHandshakeFinish()) { 
			dataConn_->setSSLHandshakeCallback(
				[=](const std::shared_ptr<TcpConnection>& conn) {
					task();
				});
		}
		else {	// 非ssl连接或握手已完成
			task();
		}
	}
	else { // 连接未建立
		taskList_.emplace_back(task);
	}
}

FTPSession::FTPSession(const std::shared_ptr<TcpConnection>& conn) :
	name_(conn->name()),
	cmdConn_(conn),
	dataConn_(nullptr),
	dataAccepter_(nullptr),
	loginState_(State::UNLOGIN),
	isTransferring_(false),
	offset_(0),
	isSSL_(false)
{
	InetAddress listenAddr(conn->localAddress(), 0);
	dataAccepter_ = std::make_unique<Accepter>(conn->getBase(), listenAddr, 1);
	// 设置回调函数
	dataAccepter_->setAcceptConnectionCallback([=](socket_t sockfd) {
		if (!dataConn_) {
			dataConn_ = std::make_unique<TcpConnection>(cmdConn_->getBase(), new Socket(sockfd), isSSL_, true);
            // 控制传输速率
			dataConn_->setIORate(TOKEN_BUCKET_TICKET_LEN, TOKEN_BUCKET_READ_RATE, TOKEN_BUCKET_WRITE_RATE,
                                   TOKEN_BUCKET_READ_BURST, TOKEN_BUCKET_WRITE_BURST);
			evutil_make_socket_nonblocking(sockfd);
			// 创建数据连接后，默认不监听读写事件。接收到RETR、STOR等指令设置回调并设置enable读写
			dataConn_->disableWrite();
			LOG_DEBUG << " recv data connection " << dataConn_->name();
			// 处理任务队列
			if (!taskList_.empty()) {
				auto task = std::move(taskList_.front());
				taskList_.pop_front();
				if (dataConn_->isSSLConnection() && !dataConn_->isSSLHandshakeFinish()) {
					dataConn_->setSSLHandshakeCallback(
						[=](const std::shared_ptr<TcpConnection>& conn) { // ssl连接但是握手未结束，设置握手结束回调
							LOG_DEBUG << dataConn_->name() << " handshake finish. start execute callback func";
							task();
						}
					);
				}
				else {	// 非ssl连接或握手已经结束
					task();
				}
			}
		}
		// 如果进入450，一般是因为重复创建dataConn。即一个dataConn还没执行任务被关闭，旧开始创建另外一个dataConn。
		//	比如在checkCmdPemission后，不判断if(dataConn_)，就会导致dataConn创建后不执行任务，与下一次dataConn的创建冲突导致450
		else {
			// send 450 to client,  data connection busy! then close sockfd and exit.
			sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_NOT_TAKEN, "Data connection is busy.");
			SockOpt::shutdown(sockfd);
		}
		}
	);
}

void FTPSession::readFileCallback(std::shared_ptr<FTPFile> file, const std::shared_ptr<TcpConnection>& conn, Buffer& inBuffer)
{
	startRecvFile(file, inBuffer);
}

void FTPSession::startSendFile(std::shared_ptr<FTPFile> file)
{
	if (!dataConn_) {
		sendFtpMsg(ReplyCode::CONNECTION_CLOSE, "Data connection close, transferring abort.");
		return;
	}
	else if (dataConn_ && !file->isWriteFinish()) {
		int n = file->read(buffer_, BLOCK_SIZE);
		if (n == 0) { // 文件大小为0的情况
			dataConn_->shutdown();
		}
		else {
			dataConn_->send(buffer_, n);
		}
	}
	else { // dataConn_ && file_->isWriteFinish
		dataConn_->shutdown(); // 关闭写端，由dataConn的closeCallback重置dataConn_、file_等变量
		//file->close();
	}
}

void FTPSession::sendFtpFile(const std::shared_ptr<FTPFile>& file)
{
	if (!dataConn_) {
		sendFtpMsg(ReplyCode::CONNECTION_CLOSE, "Data connection close, transferring abort.");
		return;
	}
	else if (dataConn_ && !file->isWriteFinish()) {
		int n = file->read(buffer_, BLOCK_SIZE);
		if (n == 0) { // 文件大小为0的情况
			dataConn_->shutdown();
		}
		else {
			dataConn_->send(buffer_, n);
		}
	}
	else { // dataConn_ && file_->isWriteFinish
		dataConn_->shutdown(); // 关闭写端，由dataConn的closeCallback重置dataConn_、file_等变量
	}
}

void FTPSession::startRecvFile(const std::shared_ptr<FTPFile>& file, Buffer& inBuffer)
{
	std::string data = inBuffer.retrieveAll();
	file->write(data.c_str(), data.size());
}

std::string FTPSession::toLocalPath(const std::string& param)
{
	std::string path;
    try
    {
#if _WIN32
		// warn: std::filesystem::path() 对中文字符（如电压不平衡）可能出现bug，导致抛出异常
        if (param.size() > 0 && (param[0] == '\\' || param[0] == '/'))
        {
            path = std::filesystem::path(fmt::format("{}{}{}", user_->getRootDir(), FILE_PATH_SEP, param)).lexically_normal().string();
        }
#else
        if (param.size() > 0 && param[0] == '/')
        {
            path = std::filesystem::path(fmt::format("{}{}{}", user_->getRootDir(), FILE_PATH_SEP, param)).lexically_normal().string();
        }
#endif
        else
        {
            path = std::filesystem::path(fmt::format("{}{}{}", user_->getCurrentWorkDir(), FILE_PATH_SEP, param)).lexically_normal().string();
        }
    }
    catch (const std::exception &err)
    {
        std::string msg = err.what();
        LOG_ERROR << msg;
        return "";
	}
	return path;
}

bool FTPSession::parseCmd(const std::string& cmdlines)
{
	std::string cmdline = "";
	size_t beg = 0, end = cmdlines.size(), CRLF = beg;
	while ((CRLF = cmdlines.find("\r\n", beg)) != std::string::npos) {
		cmdline = cmdlines.substr(beg, CRLF - beg);
		beg = CRLF + 2;
		if (cmdline == "")continue; // fix: 处理\r\n\r\n的情况
		size_t sep = cmdline.find(" ");
		std::string cmd, param;
		if (sep != std::string::npos) {
			cmd = cmdline.substr(0, sep);
			param = cmdline.substr(sep + 1, cmdline.size() - sep - 1);
		}
		else {
			cmd = cmdline;
			param = "";
		}
		handleCmd(cmd, param);
	}
	return true;
}

bool FTPSession::handleCmd(std::string cmd, const std::string& param)
{
	LOG_DEBUG << "Command from " << cmdConn_->name() << " cmd: " << cmd << " " << param;
	std::for_each(cmd.begin(), cmd.end(), [](char& c) {c = std::toupper(c); });
	// note: 这里不能定义成static const，因为每个对象的this都是不同的，如果使用了static const会导致所有的this是同一个TcpConnection，导致所有的信息都发送到一个TcpConnection去。
	// bug: static const 导致的所有的信息都发送到一个TcpConnection去
	const std::map<std::string, std::function<bool(const std::string&)>> handleMap = {
		// ACCESS CONTROL COMMANDS
		{"AUTH",std::bind(&FTPSession::handleAUTH,this,std::placeholders::_1)},
		{"USER",std::bind(&FTPSession::handleUSER,this,std::placeholders::_1) },
		{"PASS",std::bind(&FTPSession::handlePASS,this,std::placeholders::_1) },
		{"QUIT",std::bind(&FTPSession::handleQUIT,this,std::placeholders::_1) },
		// TRANSFER PARAMETER COMMANDS
		{"PBSZ",std::bind(&FTPSession::handlePBSZ,this,std::placeholders::_1) },  // 设置保护区缓存，暂时不用
		{"PROT",std::bind(&FTPSession::handlePROT,this,std::placeholders::_1) },  // 设置加密模式P
		{"PASV",std::bind(&FTPSession::handlePASV,this,std::placeholders::_1) },  // PASV 进入PASV模式
		{"TYPE",std::bind(&FTPSession::handleTYPE,this,std::placeholders::_1) },  // TYPE I，设置传输文件的类型。只支持二进制，即参数I
		{"STRU",std::bind(&FTPSession::handleSTRU,this,std::placeholders::_1) },  // STRU F，File structure 把文件看成连续字节流，无内部结构。只支持F参数
		{"MODE",std::bind(&FTPSession::handleMODE,this,std::placeholders::_1) },  // MODE STREAM，只支持STREAM模式
		{"OPTS",std::bind(&FTPSession::handleOPTS,this,std::placeholders::_1) },  // OPTS，支持UTF-8模式
		{"SYST",std::bind(&FTPSession::handleSYST,this,std::placeholders::_1) },  // SYST，默认返回UNIX
		{"FEAT",std::bind(&FTPSession::handleFEAT,this,std::placeholders::_1) },  // FEAT，只支持STREAM模式
		// FTP SERVICE COMMANDS
		{"RETR",std::bind(&FTPSession::handleRETR,this,std::placeholders::_1) },  // RETRIEVE, RETR file 请求下载文件 
		{"STOR",std::bind(&FTPSession::handleSTOR,this,std::placeholders::_1) },  // STORE, STOR file 请求上传文件，如果文件存在则覆盖，不存在则创建新文件
		{"REST",std::bind(&FTPSession::handleREST,this,std::placeholders::_1)},	  // 由于重构了大部分代码，该指令还未支持。RESTART, REST offset 设置文件偏移量，用于文件上传、下载的断点续传
		{"RNFR",std::bind(&FTPSession::handleRNFR,this,std::placeholders::_1) },  // RNFR oldname 重命名文件，后面要紧跟一条RNTO语句
		{"RNTO",std::bind(&FTPSession::handleRNTO,this,std::placeholders::_1)},   // RNTO newname 重命名文件为
		{"DELE",std::bind(&FTPSession::handleDELE,this,std::placeholders::_1)},   // 删除文件
		{"PWD",std::bind(&FTPSession::handlePWD,this,std::placeholders::_1)},	  // 当前文件夹
		{"MKD",std::bind(&FTPSession::handleMKD,this,std::placeholders::_1)},	  // 创建文件夹
		{"RMD",std::bind(&FTPSession::handleRMD,this,std::placeholders::_1)},	  // 删除文件夹
		{"CWD",std::bind(&FTPSession::handleCWD,this,std::placeholders::_1)},	  // 打开文件夹 change work directory
		{"CDUP",std::bind(&FTPSession::handleCDUP,this,std::placeholders::_1)},	  // 回到上级目录
		{"LIST",std::bind(&FTPSession::handleLIST,this,std::placeholders::_1) },
		{"SIZE",std::bind(&FTPSession::handleSIZE,this,std::placeholders::_1)},   // SIZE, SIZE file 获取文件大小，用于文件上传、下载的断点续传
		{"NOOP",std::bind(&FTPSession::handleNOOP,this,std::placeholders::_1)},	  // 什么都不做，返回ok
		{"HELP",std::bind(&FTPSession::handleHELP,this,std::placeholders::_1) }
	};
	auto handler = handleMap.find(cmd);
	if (handler == handleMap.end()) {
		sendFtpMsg(ReplyCode::SYNTAX_ERROR_UNRECOGNIZED_COMMAND, "Command not implemented");
		return false;
	}
	// 检查用户是否有指令的权限，在LIST返回文件列表的时候处理文件权限的 rw
	if (!checkCmdPermission(cmd)) {
		sendFtpMsg(ReplyCode::PERMISSION_DENIED, "Command permission denied");
		if(cmd == "RETR" || cmd== "LIST" || cmd == "STOR"){
			// 如果是retr、stor、list等需要建立数据连接的指令，需要关闭即将到来的数据连接
			auto denyTask = [=]() {
				if (dataConn_) {
					LOG_DEBUG << "Command Denied: " << cmd << ", close data connection: " << dataConn_->name();
					dataConn_->shutdown();
					dataConn_.reset();
					dataConn_ = nullptr;
				}
				};
			// 这里需要if判断是否已经建立dataConn。如果不加if，先创建连接，后发送指令的情况: 
			// 这种情况下，建立数据连接时任务队列还没有将denyTask丢进去，而发送stor、retr等指令后尝试dataConn连接执行denyTask时，会将denyTask丢入任务队列，然后建立dataConn执行denyTask，
			// 但是dataConn已经被提前创建，导致返回425。而丢入任务队列的denyTask无人执行，会在下一个dataConn到来时执行，导致后面的指令异常。
			if (dataConn_ && !isTransferring_) {
				// 如果isTransffering_==true，说明该dataConn属于上一条指令且还在传输数据，不能直接关闭dataConn。
				// 不过这种上一条指令未完成，就开始下一条指令传送的行为本身是有问题的，应该是用一个任务队列按顺序执行指令
				denyTask();
			}
			else {
				if (cmd == "RETR" || cmd == "LIST") {
					taskList_.emplace_back(denyTask);
				}
				if (cmd == "STOR") {
					taskList_.emplace_front(denyTask);
				}
			}
		}
		return false;
	}
	
	if (!handler->second(param)) {
		return false;
	}
	lastCommand_.command_ = cmd;
	lastCommand_.param_ = param;
	return true;
}

FTPSession::~FTPSession()
{
	if (user_ && loginState_ == State::LOGINED) {
		UserManager::instance().userQuit(user_->getUsername());
	}
	dataConn_.reset();
	dataConn_ = nullptr;
	cmdConn_.reset();
	cmdConn_ = nullptr;
	if (buffer_)delete[] buffer_;
}

bool FTPSession::handleAUTH(const std::string& param)
{
#ifdef JLFTP_ENABLE_OPENSSL
	std::string authParam = param;
	std::for_each(std::begin(authParam), std::end(authParam), [&](char& c) {c = std::toupper(c); });
	if(authParam =="TLS" || authParam == "SSL") {
		// BUG: tcpconntion切换成ssl tcpconnection存在bug，会导致程序死掉
		//cmdConn_->switchToSSL();
		// reason: 原因是调用sendFtpMsg()后立即调用cmdConn_->switchToSSL()会导致当前buffer的信息还没处理，但是已经切换到ssl了
		//		进而导致客户端收不到AUTH command ok. Expecting TLS negotiation.响应
		// fix: 设置cmdConn_的写完成回调，在写完AUTH指令的回复后才调用switchToSSL()切换成ssl连接，
		//		然后将原来的写完成回调设置回去
		WriteCompleteCallback cb = cmdConn_->getWriteCompleteCallback();
		cmdConn_->setWriteCompleteCallback([=](const std::shared_ptr<TcpConnection>& conn) {
			conn->switchToSSL();
			isSSL_ = true;
			conn->setWriteCompleteCallback(cb);
			}
		);
		sendFtpMsg(ReplyCode::AUTH_OPENSSL_SUCCESSFUL, "AUTH command ok. Expecting TLS negotiation.");
	}
	return true;
#else
	sendFtpMsg(ReplyCode::SYNTAX_ERROR_UNRECOGNIZED_COMMAND, "Command not implemented");
	return false;
#endif // ENABLE_OPENSSL
}

bool FTPSession::handleUSER(const std::string& param)
{
	if (checkLoginState()) {
		sendFtpMsg(ReplyCode::REQUESTED_ACTION_ABORTED, "User has login.");
		return true;
	}
	bool existed = UserManager::instance().exist(param);
	if (existed) {
		loginState_ = State::LOGINING;
		sendFtpMsg(ReplyCode::USER_NAME_OK, "Please enter password.");
		return true;
	}
	// note: 匿名登录的处理, 
	/* 
	c: USER anonymous
	s: 226 Please enter password.
	c: PASS ****
	s: 530 Error password.
    */
	else if (param == "anonymous") // 匿名登录
    {
        sendFtpMsg(ReplyCode::USER_NAME_OK, "Please enter password.");
        return true;
    }
	else {
        sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Unknown username.");
		return false;
	}
}

bool FTPSession::handlePASS(const std::string& param)
{
	if (checkLoginState()) {
		sendFtpMsg(ReplyCode::REQUESTED_ACTION_ABORTED, "User has login.");
		return false;
	}
	if (lastCommand_.command_ != "USER") {
		sendFtpMsg(ReplyCode::BAD_SEQUENCE_OF_COMMANDS, "Bad sequence of command.");
		return false;
	}
	std::string username = lastCommand_.param_;
	bool pass = UserManager::instance().userLogin(username, param);
	if (pass) {
		user_ = std::make_unique<FTPUser>(*UserManager::instance().getUser(username));
		user_->setCurrentWorkDir(fmt::format("{}", FILE_PATH_SEP));
		loginState_ = State::LOGINED;
		sendFtpMsg(ReplyCode::USER_LOGGED_IN, "Login successful.");
		return true;
	}
	else {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "Error password.");
		return false;
	}
}

bool FTPSession::handleQUIT(const std::string& param)
{
	sendFtpMsg(ReplyCode::USER_LOGGED_OUT, "Service close control connection.");
	cmdConn_->shutdown();
	return true;
}

bool FTPSession::handlePROT(const std::string& param)
{
#ifdef JLFTP_ENABLE_OPENSSL
	std::string isPriv = param;
	std::for_each(isPriv.begin(), isPriv.end(), [](char& c) {c = std::toupper(c); });
	if (isPriv == "P") {
		sendFtpMsg(ReplyCode::COMMAND_OK, "PROT now Private.");
	}
	else {
		sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Unrecognized param");
	}
	return true;
#endif // JLFTP_ENABLE_OPENSSL
	return false;
}

bool FTPSession::handlePBSZ(const std::string& param)
{
#ifdef JLFTP_ENABLE_OPENSSL
	std::string size = param;
	if (size == "0") {
		sendFtpMsg(ReplyCode::COMMAND_OK, "PBSZ set to 0.");
	}
	else {
		sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Unrecognized param");
	}
	return true;
#endif // JLFTP_ENABLE_OPENSSL
	return false;
}

bool FTPSession::handlePASV(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	if (!dataAccepter_->startListen()) {
		sendFtpMsg(ReplyCode::REQUESTED_ACTION_ABORTED, "Error entering PASV mode.");
		LOG_ERROR << "Error entering PASV mode.";
		return false;
	}
	std::string listenIp = dataAccepter_->getIPv4Address();
	std::replace(listenIp.begin(), listenIp.end(), '.', ',');
	uint16_t listenPort = dataAccepter_->getPort();
	LOG_DEBUG << "listen data connection port: " << listenPort;
	unsigned short high = (listenPort >> 8) & (0xFF);
	unsigned short low = listenPort & 0xFF;
	std::string passiveMsg = fmt::format("Entering passive mode ({},{},{})", listenIp, high, low);
	sendFtpMsg(ReplyCode::ENTERING_PASSIVE_MODE, passiveMsg);
	return true;
}

bool FTPSession::handleSYST(const std::string& param)
{
	// 统一支持UNIX风格的，主要针对LIST返回的文件列表
	sendFtpMsg(ReplyCode::SYSTEM_NAME, "UNIX");
	return true;
}

bool FTPSession::handleFEAT(const std::string& param)
{
	std::stringstream ss;
	ss << "211-Features:\r\n";
	ss << " UTF8\r\n";
	ss << " PASV\r\n";
	ss << "211 End\r\n";
	std::string featMsg = ss.str();
	//sendFtpMsg(ReplyCode::COMMAND_OK, featMsg);
	cmdConn_->send(featMsg.c_str(), featMsg.size());
	return true;
}

bool FTPSession::handleMODE(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string mode = param;
	std::for_each(mode.begin(), mode.end(), [](char& c) {c = std::toupper(c); });
	if (mode == "STREAM" || mode == "") {
		sendFtpMsg(ReplyCode::FILE_STATUS_OK, "Switching to STREAM mode.");
		return true;
	}
	sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Unrecognized param");
	return false;
}

bool FTPSession::handleOPTS(const std::string& param)
{
    std::string _param = param;
    std::for_each(_param.begin(), _param.end(), [](char &c)
                  { c = std::tolower(c); });
    if (_param == "utf8 on" || _param.empty())
    {
		sendFtpMsg(ReplyCode::COMMAND_OK, "UTF8 mode enabled");
		return true;
	}
	sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Unrecognized param");
	return false;
}

bool FTPSession::handleTYPE(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string type = param;
	std::for_each(type.begin(), type.end(), [](char& c) {c = std::toupper(c); });
	if (type == "I" || type == "") {
		sendFtpMsg(ReplyCode::COMMAND_OK, "Switching to Binary type.");
		return true;
	}
	else if (type == "A") {
		sendFtpMsg(ReplyCode::COMMAND_OK, "Switching to ASCII type.");
		return true;
	}
	sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Param unrecognized.");
	return false;
}

bool FTPSession::handleSTRU(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string structure = param;
	std::for_each(structure.begin(), structure.end(), [](char& c) {c = std::toupper(c); });
	if (structure == "F" || structure == "") {
		sendFtpMsg(ReplyCode::FILE_STATUS_OK, "Switching to file structure.");
		return true;
	}
	sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Param unrecognized.");
	return false;
}

bool FTPSession::handleLIST(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	// 正在传输其他文件，禁止传输
	if (isTransferring_)
	{
		sendFtpMsg(ReplyCode::NOT_OPEN_DATA_CONNECTION, "Data connection is transferring.");
		return false;
	}
	std::string listDir;
	// param以/或\\开头
	if (param == "-l" || param == "-a" || param == "-la") {
		listDir = "./";
	}
	else {
		listDir = param;
	}
	std::string filename = toLocalPath(listDir);
    if (filename == "" || !checkFilePermission(filename))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "Access file failed.");
		return false;
	}
	std::string info;
	// 选择性处理param参数中的 -a -l 等等选项
	bool success = FTPUtil::getFileInfo(filename, info, user_->getPermission());
	if (!success) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File not find or no access.");
		return false;
	}
	sendFtpMsg(ReplyCode::FILE_STATUS_OK, "About to open data connection.");
	auto listTask = [=]() {
		dataConn_->enableWrite();
		dataConn_->setWriteCompleteCallback(
			[&](const std::shared_ptr<TcpConnection>& conn) {
				dataConn_->shutdown();
				isTransferring_ = false;
				// note: 设置close connection之后 cmdConn_ 才发送完成指令
                dataConn_->setCloseCallback([&](const std::shared_ptr<TcpConnection>& conn) {
					dataConn_.reset();
					dataConn_ = nullptr;
					sendFtpMsg(ReplyCode::CLOSING_DATA_CONNECTION, "Directory send OK.");
					});
			}
		);
		isTransferring_ = true;
		LOG_DEBUG << "start handle list, list info: "<< info;
		dataConn_->send(info.c_str(), info.size());
		//LOG_DEBUG << "获取的文件信息: " << info;
		};
	handleDataConnTask(listTask);
	return true;
}

bool FTPSession::handleSTOR(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	// 正在传输其他文件，禁止传输
	if (isTransferring_)
	{
		sendFtpMsg(ReplyCode::NOT_OPEN_DATA_CONNECTION, "Data connection is transferring.");
		return false;
	}
	std::string filename = toLocalPath(param);
    if (filename == "" || !checkFilePermission(filename))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "Access file failed.");
		return false;
	}
	auto file = std::make_shared<FTPFile>();
	if (!file->open(filename, "wb")) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "Access file failed.");
		file->close(); // 打开失败，关闭文件
		return false;
	}
	sendFtpMsg(ReplyCode::FILE_STATUS_OK, "About to open data connection.");
	auto storTask = [=]() {
		dataConn_->enableRead();
		dataConn_->setCloseCallback([=](const std::shared_ptr<TcpConnection>& conn) {
			LOG_DEBUG << "File " << file->getFilename() << " STOR successful. "<< "dataConnection " << dataConn_->name() << " close.";
			dataConn_.reset();
			dataConn_ = nullptr;
			isTransferring_ = false;
			// TODO: 在close回调设置dataConn为nullptr，保证ssl关闭后才将dataConn_置为nullptr
			// 这里已经是设置的closecallback了，考虑将该dataConn_的重置放在传输结束时，closecallback向指令连接发送 done 响应
			//dataConn_->setCloseCallback();
			sendFtpMsg(ReplyCode::COMMAND_OK, "STOR command done.");
			}
		);
		LOG_DEBUG << dataConn_->name() << " start STOR " << file->getFilename();
		dataConn_->setMessageCallback(std::bind(&FTPSession::readFileCallback, this, file, std::placeholders::_1, std::placeholders::_2));
		isTransferring_ = true;
		};
	handleDataConnTask(storTask);
	return true;
}

bool FTPSession::handleRETR(const std::string& param)
{
	//std::cout << "this working thread: "  << std::this_thread::get_id() << std::endl;
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	if (isTransferring_) // 正在传输其他文件，禁止传输
	{
		// note: 数据连接忙碌需要返回450表示数据连接被占用，返回其他错误码（如425）则会导致客户端执行其他操作导致异常
		sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_NOT_TAKEN, "Data Connection is busy, requested file action not taken.");
		return false;
	}
	std::string filename = toLocalPath(param);
    if (filename == "" || !checkFilePermission(filename))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "Access file failed.");
		return false;
	}
	if (!FTPUtil::exist(filename)) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File not find.");
		return false;
	}
	auto file = std::make_shared<FTPFile>();
	if (!file->open(filename, "rb")) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "Access file failed.");
		file->close(); // 打开失败，关闭文件
		return false;
	}
	sendFtpMsg(ReplyCode::FILE_STATUS_OK, "About to open data connection.");
	// 需要考虑两种情况: 1、数据连接建立好时，retr指令如何响应 2、数据连接尚未建立，retr指令如何响应
	// 1、数据连接建立好时，立即开始传送文件 2、未建立，则将任务丢入任务队列队尾，创建数据连接后走连接建立回调处理任务
	auto retrTask = [=]() {
		dataConn_->enableWrite();
		dataConn_->setCloseCallback([=](const std::shared_ptr<TcpConnection>& conn) {
			LOG_DEBUG << "File " << file->getFilename() << " transfer successful. dataConnection " << dataConn_->name() << "close.";
			sendFtpMsg(ReplyCode::COMMAND_OK, "RETR command done.");
			dataConn_.reset();
			dataConn_ = nullptr;
			// TODO: 在close回调设置dataConn为nullptr，保证ssl关闭后才将dataConn_置为nullptr。
			// 这里已经是设置的closecallback了，考虑将该dataConn_的重置放在传输结束时，closecallback向指令连接发送 done 响应
			//dataConn_->setCloseCallback();
			isTransferring_ = false;
			}
		);
		dataConn_->setWriteCompleteCallback([=](const std::shared_ptr<TcpConnection>& conn) {
			startSendFile(file);
			}
		);
		LOG_DEBUG << dataConn_->name() << " start transferring file " << file->getFilename();
		isTransferring_ = true;
		startSendFile(file);
		};
	handleDataConnTask(retrTask);
	return true;
}

bool FTPSession::handleSIZE(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string filename = toLocalPath(param);
    if (filename == "" || !checkFilePermission(filename))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "Access file failed.");
		return false;
	}	std::string temp = std::filesystem::path(filename).lexically_normal().string();
	if (!FTPUtil::exist(filename)) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File not find.");
		return false;
	}
	size_t fileSize = 0;
	if (!FTPUtil::getFileSize(filename, fileSize)) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "Get file size failed.");
		return false;
	}
	sendFtpMsg(ReplyCode::FILE_STATUS, std::to_string(fileSize));
	return true;
}

bool FTPSession::handleREST(const std::string& param)
{
	sendFtpMsg(ReplyCode::COMMAND_NOT_IMPLEMENTED, "REST not support.");
	return false;

	//// todo: 重构rest指令
	//if (!checkLoginState()) {
	//	sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
	//	return false;
	//}
	//size_t offset = 0;
	//try {
	//	offset = std::stoll(param);
	//}
	//catch (const std::exception& err) {
	//	sendFtpMsg(ReplyCode::SYNTAX_ERROR_PARAMETERS, "Error param.");
	//	return false;
	//}
	//offset_ = offset;
	//std::string msg = fmt::format("Restarting at {}. Send STOR or RETR to initiate transfer.", offset);
	//sendFtpMsg(ReplyCode::FILE_ACTION_NEED_MORE_INFORMATION, msg);
	//return true;
}

bool FTPSession::handlePWD(const std::string& param)
{
	// note: PWD返回的路径是'/'或'\'开头的
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string workDir = user_->getWorkDir();
#if _WIN32
	if (workDir.size() > 0 && workDir[0] != '\\' && workDir[0] != '/') {
		workDir = "/" + workDir;
	}
#else
	if (workDir.size() > 0 && workDir[0] != '/') {
		workDir = "/" + workDir;
	}
#endif 
	std::string replyMsg = fmt::format("\"{}\" is the current directory.", workDir);
	sendFtpMsg(ReplyCode::PATHNAME_CREATED, replyMsg);
	return true;
}

bool FTPSession::handleCWD(const std::string& param)
{
	// note: 注意param是否以\\或/开头，判断请求的文件是否由根目录开始找
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string destDir = toLocalPath(param);
    if (destDir == "" || !checkFilePermission(destDir))
    {
		LOG_ERROR << "No permission: " << destDir;
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File not found or no access.");
		return false;
	}
	if (!FTPUtil::exist(destDir)) {
		LOG_ERROR << "Error path: " << destDir;
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File not found or no access.");
		return false;
	}
	std::string workDir = destDir.substr(user_->getRootDir().size());
	user_->setCurrentWorkDir(workDir);
	if (workDir == "")workDir = "/";
    std::replace(workDir.begin(), workDir.end(), '\\', '/');
	std::string replyMsg = fmt::format("Change work dir to {}.", workDir);
	sendFtpMsg(ReplyCode::COMMAND_OK, replyMsg);
	return true;
}

bool FTPSession::handleCDUP(const std::string& param)
{
	return handleCWD("..");
}

bool FTPSession::handleNOOP(const std::string& param)
{
	sendFtpMsg(ReplyCode::COMMAND_OK, "Command ok.");
	return true;
}


bool FTPSession::handleMKD(const std::string& param)
{
	// note: filezilla会先使用cwd进入工作目录，在工作目录内传递目标目录名（不带路径）
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string dirname = toLocalPath(param);
    if (dirname == "" || !checkFilePermission(dirname))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File rename denied.");
		return false;
	}
	if (FTPUtil::exist(dirname)) {
		sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_OK, "Dir has existed.");
		return true;
	}
	if (!FTPUtil::mkdir(dirname)) {
		sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_NOT_TAKEN, "Create Directory denied.");
		return false;
	}
	sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_OK, "Create Directory successfully.");
	return true;
}

bool FTPSession::handleRMD(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string dirname = toLocalPath(param);
    if (dirname == "" || !checkFilePermission(dirname))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File rename denied.");
		return false;
	}
	if (!FTPUtil::exist(dirname)) {
		sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_OK, "Dir not existed.");
		return true;
	}
	if (!FTPUtil::rmdir(dirname)) {
		sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_NOT_TAKEN, "Remove Directory denied.");
		return false;
	}
	sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_OK, "Remove Directory successfully.");
	return true;
}

bool FTPSession::handleRNFR(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string filename = toLocalPath(param);
    if (filename == "" || !checkFilePermission(filename))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File rename denied.");
		return false;
	}
	if (!FTPUtil::exist(filename)) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File not find.");
		return false;
	}
	sendFtpMsg(ReplyCode::FILE_ACTION_NEED_MORE_INFORMATION, "Please enter new filename.");
	return true;
}

bool FTPSession::handleRNTO(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	if (lastCommand_.command_ != "RNFR") {
		sendFtpMsg(ReplyCode::BAD_SEQUENCE_OF_COMMANDS, "Bad sequence of command, last command must be RNFR.");
		return false;
	}

	std::string oldName = toLocalPath(lastCommand_.param_);
	std::string newName = toLocalPath(param);
    if (oldName == "" || newName == "" || !checkFilePermission(oldName) || !checkFilePermission(newName))
    {
		sendFtpMsg(ReplyCode::ILLEGAL_FILE_NAME, "File name illegal.");
		return false;
	}
	bool res = FTPUtil::tryReName(oldName, newName);
	if (!res) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File rename denied.");
		return false;
	}
	sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_OK, "Rename file successfully.");
	return true;
}

bool FTPSession::handleDELE(const std::string& param)
{
	if (!checkLoginState()) {
		sendFtpMsg(ReplyCode::NOT_LOGGED_IN, "User not login.");
		return false;
	}
	std::string filename = toLocalPath(param);
    if (filename == "" || !checkFilePermission(filename))
    {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File delete permission denied.");
		return false; // checkPermisson函数需要修改
	}
	if (!FTPUtil::exist(filename)) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File not find.");
		return false;
	}
	if (!FTPUtil::tryDelete(filename)) {
		sendFtpMsg(ReplyCode::FILE_NOT_FIND_OR_NO_ACCESS, "File delete denied.");
		return false;
	}
	sendFtpMsg(ReplyCode::REQUESTED_FILE_ACTION_OK, "File delete successfully.");
	return true;
}

bool FTPSession::handleHELP(const std::string& param)
{
	sendFtpMsg(ReplyCode::COMMAND_NOT_IMPLEMENTED, "Command not implemented.");
	return false;
}